package tree;

import java.util.Arrays;
import java.util.Iterator;

import compiler.CompilerException;

import code.CodeList;
import code.TargetCode;
import lexer.Token;

public class MyTree implements Tree {

	private Token token;
	private Tree parent;
	private Tree[] operands;
	
	public MyTree(Token token, Tree ... operands) {
		this.token = token;
		this.operands = operands;
		for (Tree t : this.operands) {
			t.setParent(this);
		}
	}
	
	/* (non-Javadoc)
	 * @see tree.Tree#analyzeNames(tree.IdentifierScope, tree.MethodScope, tree.ClassScope)
	 */
	public void analyzeNames(IdentifierScope identScope,
			MethodScope methodScope, ClassScope classScope)
			throws NamingException {
		for (Tree t : this.operands)
			t.analyzeNames(identScope, methodScope, classScope);
	}

	/* (non-Javadoc)
	 * @see tree.Tree#computeType()
	 */
	public Type computeType() throws TypingException {
		for (Tree t : this.operands)
			t.computeType();
		return new Type(DataType.VOID, "");
	}

	/* (non-Javadoc)
	 * @see tree.Tree#genCode(code.TargetCode)
	 */
	public void generateCode(CodeList code) throws CompilerException {
		for (Tree t : this.operands)
			t.generateCode(code);
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getInstVar()
	 */
	public int getInstVar() {
		return -1;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getLocalVar()
	 */
	public int getLocalVar() {
		return -1;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getMethodVar()
	 */
	public int getMethodVar() {
		return -1;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getOperand(int)
	 */
	public Tree getOperand(int i) {
		return this.operands[i];
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getOperands()
	 */
	public int getOperands() {
		return this.operands.length;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getParent()
	 */
	public Tree getParent() {
		return this.parent;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getToken()
	 */
	public Token getToken() {
		return this.token;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#iterator()
	 */
	public Iterator<Tree> iterator() {
		return Arrays.asList(this.operands).iterator();
	}

	/* (non-Javadoc)
	 * @see tree.Tree#preAnalyzeNames(tree.IdentifierScope, tree.MethodScope, tree.ClassScope)
	 */
	public void preAnalyzeNames(IdentifierScope identScope,
			MethodScope methodScope, ClassScope classScope)
			throws NamingException {
		for (Tree t : this.operands)
			t.preAnalyzeNames(identScope, methodScope, classScope);
	}

	/* (non-Javadoc)
	 * @see tree.Tree#setOperand(int, tree.Tree)
	 */
	public void setOperand(int i, Tree t) {
		this.operands[i] = t;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#setParent(tree.Tree)
	 */
	public void setParent(Tree t) {
		this.parent = t;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		String result = nodeToString();
		String prefix = "(";
		for (Tree t : this.operands) {
			result += prefix;
			result += t.toString();
			prefix = ", ";
		}
		if (getOperands() > 0) {
			result += ")";
		}
		return result;
	}
	
	public String nodeToString() {
		return this.token.toString();
	}

	/* (non-Javadoc)
	 * @see tree.Tree#generateProgramCode(code.TargetCode)
	 */
	public void generateProgramCode(TargetCode targetCode) throws CompilerException {
		for (Tree t : this.operands)
			t.generateProgramCode(targetCode);
	}

	/* (non-Javadoc)
	 * @see tree.Tree#expectSameType(tree.Type, tree.Type)
	 */
	public Type expectSameType(Tree t1, Tree t2) throws TypingException {
		Type typ1 = t1.computeType();
		Type typ2 = t2.computeType();
		if (typ1.equals(typ2)) {
			return typ1;
		}
		throw TypingException.notEqual(t1.getParent(), t1, t2, typ1, typ2);
	}

	/* (non-Javadoc)
	 * @see tree.Tree#expectType(tree.Tree, tree.Type)
	 */
	public Type expectType(Tree t, Type ... ty) throws TypingException {
		Type typ = t.computeType();
		boolean contains = false;
		for (Type type : ty) {
			if (typ.equals(type)) {
				contains = true;
			}
		}
		if (!contains) {
			throw TypingException.expected(t, typ, ty);
		}
		return typ;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getNumberOfInstVar()
	 */
	public int getNumberOfInstVar() {
		return -1;
	}

	/* (non-Javadoc)
	 * @see tree.Tree#getAllOperands()
	 */
	public Tree[] getAllOperands() {
		return this.operands;
	}
	
}
